package com.apestech.api.workflow.lib.element;

import com.apestech.api.workflow.callback.FlowQueryResult;
import com.apestech.framework.db.SqlSession;
import com.apestech.framework.db.queryData.QueryDate;
import com.apestech.framework.json.JSONArray;
import com.apestech.framework.json.JSONObject;
import com.apestech.framework.util.DBUtil;
import com.apestech.framework.util.StringUtil;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.identitylink.api.IdentityLink;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import java.util.*;

/**
 * 工作流查询
 *
 * @author hup
 * @create 2018-07-04-10:19
 */
public class FlowQuery extends StringUtil {

    private WorkProcessEngine engine;
    private boolean lazy=true;
    private String varType="object";
    private FlowQueryResult flowQueryResult;
    private SqlSession session;

    public FlowQuery(WorkProcessEngine engine) throws Exception {
        this.engine=engine;
        this.session= DBUtil.getSession(this.engine.getDataSource());
        this.session.setConnection(this.engine.getConnection());
    }

    public void setLazy(boolean lazy){
        this.lazy=lazy;
    }

    public void setVarType(String varType){
        this.varType=varType;
    }

    public void setFlowQueryResult(FlowQueryResult flowQueryResult) {
        this.flowQueryResult = flowQueryResult;
    }

    /**
     * 获取某个任务下的变量信息
     * @param taskId
     * @return
     */
    public JSONObject getVar(String taskId){
        return getVar(new JSONObject(), taskId);
    }

    private JSONObject getVar(JSONObject row, String taskId){
        JSONObject taskInfoMap = new JSONObject(engine.getTaskService().getVariables(taskId));
        if(taskInfoMap.get("businessKey")!=null){
            row.put("businessKey", taskInfoMap.get("businessKey"));
            taskInfoMap.remove("businessKey");
        }
        if("object".equals(varType)){
            row.put("processVariables",taskInfoMap);
        } else {
            row.putAll(taskInfoMap);
        }
        return row;
    }

    private JSONObject getVarMap(Task task, JSONObject row) throws Exception {
        if(!lazy){
            row=getVar(row, task.getId());
        }
        if(flowQueryResult!=null){
            row=flowQueryResult.queryData(task, row);
        }
        return row;
    }

    private Object execQry(Object query, int size, int page){
        FlowQueryCondition condition=new FlowQueryCondition(query, null);
        return condition.execQry(size, page);
    }

    /**
     * 用户待办任务
     * @param userId 用户ID
     * @param param 查询条件
     * @return
     * @throws Exception
     */
    public JSONArray getUserToDoTask(String userId, JSONObject param) throws Exception {
        return getUserToDoTask(userId, param, 0, 0);
    }

    public JSONArray getUserToDoTask(String userId, JSONObject param, int size, int page) throws Exception {
        JSONArray result=new JSONArray();
        TaskQuery q=engine.getTaskService().createTaskQuery().taskCandidateOrAssigned(userId);
        List<Task> tasks= (List<Task>) execQry(q, size, page);
        for(Task task : tasks){
            JSONObject taskObj = new JSONObject();
            taskObj.put("processDefinitionId",task.getProcessDefinitionId());
            taskObj.put("taskDefinitionKey",task.getTaskDefinitionKey());
            taskObj.put("taskName",task.getName());
            taskObj.put("processInstanceId",task.getProcessInstanceId());
            taskObj.put("createTime",task.getCreateTime());
            taskObj.put("claimTime",task.getClaimTime());
            taskObj.put("dueDate",task.getDueDate());
            taskObj.put("taskId",task.getId());
            taskObj.put("state", task.getDescription());
            taskObj=getVarMap(task, taskObj);
            result.add(taskObj);
        }
        return result;
    }

    /**
     * 用户组待办任务
     * @param candidateGroup 组
     * @param param 查询条件
     * @return
     * @throws Exception
     */
    public JSONArray getUserGroupToDoTask(String candidateGroup, JSONObject param) throws Exception{
        return getUserGroupToDoTask(candidateGroup, param, 0, 0);
    }

    public JSONArray getUserGroupToDoTask(String candidateGroup, JSONObject param, int size, int page) throws Exception{
        JSONArray result = new JSONArray();
        TaskQuery q=engine.getTaskService().createTaskQuery().taskCandidateGroup(candidateGroup);
        List<Task> tasks= (List<Task>) execQry(q, size, page);
        for(Task task : tasks){
            JSONObject taskObj = new JSONObject();
            taskObj.put("processDefinitionId",task.getProcessDefinitionId());
            taskObj.put("taskDefinitionKey",task.getTaskDefinitionKey());
            taskObj.put("taskName",task.getName());
            taskObj.put("processInstanceId",task.getProcessInstanceId());
            taskObj.put("taskId",task.getId());
            taskObj.put("state", task.getDescription());
            taskObj=getVarMap(task, taskObj);
            result.add(taskObj);
        }
        return result;
    }

    /**
     * 多用户组待办任务
     * @param candidateGroups 组
     * @param param 查询条件
     * @return
     * @throws Exception
     */
    public JSONArray getUserGroupToDoTask(List<String> candidateGroups, JSONObject param) throws Exception{
        return getUserGroupToDoTask(candidateGroups, param, 0, 0);
    }

    public JSONArray getUserGroupToDoTask(List<String> candidateGroups, JSONObject param, int size, int page) throws Exception{
        JSONArray result = new JSONArray();
        TaskQuery q=engine.getTaskService().createTaskQuery().taskCandidateGroupIn(candidateGroups);
        List<Task> tasks= (List<Task>) execQry(q, size, page);
        for(Task task : tasks){
            JSONObject taskObj = new JSONObject();
            taskObj.put("processDefinitionId",task.getProcessDefinitionId());
            taskObj.put("taskDefinitionKey",task.getTaskDefinitionKey());
            taskObj.put("taskName",task.getName());
            taskObj.put("processInstanceId",task.getProcessInstanceId());
            taskObj.put("taskId",task.getId());
            taskObj.put("state", task.getDescription());
            taskObj=getVarMap(task, taskObj);
            result.add(taskObj);
        }
        return result;
    }

    /**
     * 获取运行中的流程信息
     * @param businessKey 单号
     * @param param 查询条件
     * @return
     * @throws Exception
     */
    public JSONObject getRunningProcessInfo(String businessKey, JSONObject param)throws Exception{
        JSONObject result = new JSONObject();
        TaskQuery q=engine.getTaskService().createTaskQuery().processInstanceBusinessKey(businessKey);
        getWhereTask(q, param);
        Task task =q.singleResult();
        if(task!=null){
            result.put("processDefinitionId",task.getProcessDefinitionId());
            result.put("taskDefinitionKey",task.getTaskDefinitionKey());
            result.put("taskName",task.getName());
            result.put("taskDescription",task.getDescription());
            result.put("taskId",task.getId());
            result.put("state", task.getDescription());
            result=getVarMap(task, result);
        }
        return result;
    }

    public JSONArray getTaskList(JSONObject param) throws Exception {
        return (JSONArray) getTaskList(param, 0, 0).get("rows");
    }

    public JSONObject getTaskList(JSONObject param, int size, int page) throws Exception {
        JSONArray result=new JSONArray();
        HistoricTaskInstanceQuery q=engine.getHistoryService().createHistoricTaskInstanceQuery();
        getWhereTask(q, param);
        List<HistoricTaskInstance> tasks= (List<HistoricTaskInstance>) execQry(q, size, page);
        for(HistoricTaskInstance task : tasks){
            JSONObject taskObj = new JSONObject();
            taskObj.put("durationInMillis",task.getDurationInMillis());
            taskObj.put("workTimeInMillis",task.getWorkTimeInMillis());
            taskObj.put("processDefinitionId",task.getProcessDefinitionId());
            taskObj.put("taskDefinitionKey",task.getTaskDefinitionKey());
            taskObj.put("taskName",task.getName());
            taskObj.put("processInstanceId",task.getProcessInstanceId());
            taskObj.put("createTime",task.getCreateTime());
            taskObj.put("claimTime",task.getClaimTime());
            taskObj.put("dueDate",task.getDueDate());
            taskObj.put("taskId",task.getId());
            taskObj.put("state", task.getDescription());
            taskObj.put("endTime", task.getEndTime());
            result.add(taskObj);
        }
        JSONObject obj=new JSONObject();
        obj.put("count", q.count());
        obj.put("rows",result);
        return obj;
    }

    public JSONArray getRunTaskList(JSONObject param) throws Exception {
        return getRunTaskList(param, 0, 0);
    }

    public JSONArray getRunTaskList(JSONObject param, int size, int page) throws Exception {
        JSONArray result=new JSONArray();
        TaskQuery q=engine.getTaskService().createTaskQuery();
        getWhereTask(q, param);
        List<Task> tasks= (List<Task>) execQry(q, size, page);
        for(Task task : tasks){
            JSONObject taskObj = new JSONObject();
            taskObj.put("processDefinitionId",task.getProcessDefinitionId());
            taskObj.put("taskDefinitionKey",task.getTaskDefinitionKey());
            taskObj.put("taskName",task.getName());
            taskObj.put("processInstanceId",task.getProcessInstanceId());
            taskObj.put("createTime",task.getCreateTime());
            taskObj.put("claimTime",task.getClaimTime());
            taskObj.put("dueDate",task.getDueDate());
            taskObj.put("taskId",task.getId());
            taskObj.put("state", task.getDescription());
            result.add(taskObj);
        }
        return result;
    }

    public JSONArray getProcList(JSONObject param) throws Exception {
        return (JSONArray) getProcList(param, 0, 0).get("rows");
    }

    public JSONObject getProcList(JSONObject param, int size, int page) throws Exception {
        JSONArray result=new JSONArray();
        HistoricProcessInstanceQuery q=engine.getHistoryService().createHistoricProcessInstanceQuery();
        getWhereTask(q, param);
        List<HistoricProcessInstance> procs= (List<HistoricProcessInstance>) execQry(q, size, page);
        for(HistoricProcessInstance proc : procs){
            JSONObject taskObj = new JSONObject();
            taskObj.put("durationInMillis",proc.getDurationInMillis());
            taskObj.put("id",proc.getId());
            taskObj.put("processDefinitionId",proc.getProcessDefinitionId());
            taskObj.put("businessKey",proc.getBusinessKey());
            taskObj.put("superProcessInstanceId",proc.getSuperProcessInstanceId());
            taskObj.put("startTime",proc.getStartTime());
            taskObj.put("endTime",proc.getEndTime());
            result.add(taskObj);
        }
        JSONObject obj=new JSONObject();
        obj.put("count", q.count());
        obj.put("rows",result);
        return obj;
    }

    public boolean getAssignTaskUser(String taskId){
        List<IdentityLink> list = engine.getTaskService().getIdentityLinksForTask(taskId);
        return list.size()>=0;
    }

    private void getWhereTask(Object query, JSONObject param) throws Exception {
        FlowQueryCondition condition=new FlowQueryCondition(query, param);
        condition.getWhere();
    }

    public String getProcessInstanceId(String processInstanceId) throws Exception {
        Task task=engine.getTaskService().createTaskQuery().processInstanceId(processInstanceId).singleResult();
        if(task==null){
            JSONObject map=new JSONObject();
            map.put("superProcessInstanceId",processInstanceId);
            JSONArray list=getProcList(map);
            int len=list.size();
            for(int i=0; i<len; i++){
                JSONObject p= (JSONObject) list.get(i);
                if(p.get("endTime")==null){
                    processInstanceId=p.getString("id");
                    return processInstanceId;
                }
            }
        }
        return processInstanceId;
    }

    public ArrayList<String> getTaskUserList(String taskId) throws Exception {
        ArrayList<String> list=new ArrayList<String>();
        try{
            List<IdentityLink> identityLinkList = engine.getTaskService().getIdentityLinksForTask(taskId);
            for (IdentityLink id : identityLinkList) {
                String userId = id.getUserId();
                if(userId!=null){
                    list.add(userId);
                }
            }
        } catch (Exception e) {
            throw new Exception("该任务结点不存在，或已被中止！");
        }
        return list;
    }

    public Date getTaskUpdateTime(String taskId) throws Exception {
        JSONObject m=new JSONObject();
        m.put("ID_", taskId);
        QueryDate qry= new QueryDate("UPDATE_TIME");
        session.getForResultSet().sql("SELECT LAST_UPDATED_TIME_ AS UPDATE_TIME FROM ACT_HI_TASKINST WHERE 1=1 AND ID_=#{ID_} ", m, qry);
        return qry.getDate();
    }

    public Date getPersonTaskTime(Task task, String userId) throws Exception {
        QueryDate qry= new QueryDate("CREATE_DATE");
        JSONObject m=new JSONObject();
        m.put("userId", userId);
        m.put("taskId", task.getId());
        session.getForResultSet().sql(
                   "SELECT MAX(CREATE_DATE) CREATE_DATE FROM OA_TASKLIST "+
                        "WHERE 1=1 AND TASK_ID=${taskId} AND INSTR(','||COUNTER_USER_ID||',',',' || ${userId} || ',')>=0 ", m, qry);
        if(qry.getDate()==null) return task.getCreateTime();
        else return qry.getDate();
    }

}
